// Header guard
#ifndef MONITOR_H
#define MONITOR_H

#include <queue>
#include <pthread.h>
#include <iostream>
#include <cstdlib>
#include "request.h"
#include "logger.h"
using namespace std;

/*
Monitor Object. All methods are protected by a pthreads mutex Variable. There are 2 locks
(so really there are 2 monitors (groups of mutually exclusive methods)) in this class.
One for the processing of request objects. Another to handle the liveClients variable. 
*/
class monitorsstf{
	public:
		// Constructor
		monitorsstf(logger *log);
		// Destructor
		~monitorsstf();
		
		/*
		Client threads call addRequest(request* req) to make a new request for disk access. 
		Input:	Pointer to object of type request. 
		Return: True if request was successfully passed to monitor. 
		*/
		bool addRequest(request* req);
		
		/*
		The scheduler thread calls scheduleRequests() repeatedly. 
		This method moves all input requests to the outputQueue, for the worker to consume.
		The order of the requests in the ouput queue is governed by some scheduling algorithm. 
		This threads job is to handle the overhead work of the scheduling algorithm whilst
		the workers and clients do work outside their respective critical sections. 
		*/
		bool scheduleRequests();
		
		/*
		The worker thread calls removeRequest to retrieve a request object.
		It then unlocks the monitor and proccesses that request (wastes some amount time),
		before calling SignalClient(), which enters back into the monitor and wakes the client
		who submitted that particular request object. 
		*/
		request* removeRequest();
		void signalClient(request* req);
		
		bool isWorkLeft();
		
		/*
		Keeping track of the number of active clients is a part of our end condition. 
		When a client starts the counter is incremented, and decremented when they complete. 
		When the getLiveClients() returns 0, there are no more requests being made, 
		so the scheduler and worker threads complete as well. 
		These methods are protected by a different lock within the monitor. 
		Note this implementation allows the functionality of 2 distinct monitor classes,
		but is less code to write. 
		*/
		int getLiveClients();
		void incLiveClients();
		void decLiveClients();

		int getReqDiff();
		int getReqProc();

		// Condition variable to for the worker to wait when there is new unscheduled inputCheck
		pthread_cond_t inputCheck;
		pthread_cond_t schedWait;
	private:		
		// Logger for debugging
		logger *log;
		char buffer[100];
		
		//Input/Output Queues 
		queue<request*> inputQ;
		queue<request*> outputQ;		
		
		//Number of clients actively submitting requests 
		int liveClients;
		
		//Number of requests made to the monitor by clients
		int reqMade;
		//Number of requests the monitor passes to the worker
		int reqProc;

		// Mutex to "fake the monitor(s)" 1 for the requests, 1 for liveClients
		pthread_mutex_t lock;
		pthread_mutex_t completeLock;
		
		//Flag that addReq signals because there is a new request made
		bool newReqFlag;
		
		// The current position of the disk head
		int lastTrack;
};
// End guard
#endif
